#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RedFire 配置验证工具
验证配置文件的完整性、有效性和一致性
"""

import os
import re
import json
import yaml
import logging
from pathlib import Path
from typing import Dict, List, Any, Optional, Tuple, Union
from dataclasses import dataclass
from datetime import datetime

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@dataclass
class ValidationResult:
    """验证结果"""
    is_valid: bool
    errors: List[str]
    warnings: List[str]
    suggestions: List[str]
    score: float  # 0-100分


@dataclass
class ConfigSchema:
    """配置模式定义"""
    name: str
    required_fields: List[str]
    optional_fields: List[str]
    field_types: Dict[str, type]
    field_patterns: Dict[str, str]  # 正则表达式模式
    field_ranges: Dict[str, Tuple[Any, Any]]  # 取值范围
    dependencies: Dict[str, List[str]]  # 字段依赖关系


class ConfigValidator:
    """配置验证器"""
    
    def __init__(self):
        self.schemas = self._load_schemas()
        self.validation_rules = self._load_validation_rules()
    
    def _load_schemas(self) -> Dict[str, ConfigSchema]:
        """加载配置模式"""
        schemas = {}
        
        # 数据库配置模式
        schemas['database'] = ConfigSchema(
            name='数据库配置',
            required_fields=['host', 'port', 'database', 'username', 'password'],
            optional_fields=['pool_size', 'max_overflow', 'timeout', 'charset'],
            field_types={
                'host': str,
                'port': int,
                'database': str,
                'username': str,
                'password': str,
                'pool_size': int,
                'max_overflow': int,
                'timeout': int,
                'charset': str
            },
            field_patterns={
                'host': r'^[\w\.-]+$',
                'database': r'^[a-zA-Z_][a-zA-Z0-9_]*$',
                'username': r'^[a-zA-Z_][a-zA-Z0-9_]*$',
                'charset': r'^[a-z0-9_-]+$'
            },
            field_ranges={
                'port': (1, 65535),
                'pool_size': (1, 100),
                'max_overflow': (0, 200),
                'timeout': (1, 300)
            },
            dependencies={}
        )
        
        # Redis配置模式
        schemas['redis'] = ConfigSchema(
            name='Redis配置',
            required_fields=['host', 'port'],
            optional_fields=['password', 'db', 'timeout', 'pool_size'],
            field_types={
                'host': str,
                'port': int,
                'password': str,
                'db': int,
                'timeout': int,
                'pool_size': int
            },
            field_patterns={
                'host': r'^[\w\.-]+$'
            },
            field_ranges={
                'port': (1, 65535),
                'db': (0, 15),
                'timeout': (1, 60),
                'pool_size': (1, 50)
            },
            dependencies={}
        )
        
        return schemas
    
    def _load_validation_rules(self) -> Dict[str, Any]:
        """加载验证规则"""
        return {
            'security': {
                'password_min_length': 8,
                'password_patterns': [
                    r'[A-Z]',  # 大写字母
                    r'[a-z]',  # 小写字母
                    r'[0-9]',  # 数字
                    r'[!@#$%^&*(),.?":{}|<>]'  # 特殊字符
                ],
                'forbidden_passwords': [
                    'password', '123456', 'admin', 'root', 'guest',
                    'redfire', 'redfire123', 'change_me'
                ]
            },
            'network': {
                'reserved_ports': [22, 80, 443, 3306, 5432],
                'private_ip_ranges': [
                    r'^10\.',
                    r'^172\.(1[6-9]|2[0-9]|3[0-1])\.',
                    r'^192\.168\.'
                ]
            }
        }
    
    def validate_config_file(self, file_path: str, config_type: str = None) -> ValidationResult:
        """验证配置文件"""
        result = ValidationResult(
            is_valid=True,
            errors=[],
            warnings=[],
            suggestions=[],
            score=100.0
        )
        
        try:
            # 检查文件是否存在
            if not os.path.exists(file_path):
                result.errors.append(f"配置文件不存在: {file_path}")
                result.is_valid = False
                result.score = 0.0
                return result
            
            # 读取配置文件
            config_data = self._load_config_file(file_path)
            if config_data is None:
                result.errors.append(f"配置文件格式错误: {file_path}")
                result.is_valid = False
                result.score = 0.0
                return result
            
            # 自动检测配置类型
            if config_type is None:
                config_type = self._detect_config_type(config_data, file_path)
            
            # 验证配置结构
            structure_result = self._validate_structure(config_data, config_type)
            self._merge_results(result, structure_result)
            
            # 验证配置值
            value_result = self._validate_values(config_data, config_type)
            self._merge_results(result, value_result)
            
            # 验证安全性
            security_result = self._validate_security(config_data, config_type)
            self._merge_results(result, security_result)
            
            # 计算最终得分
            result.score = self._calculate_score(result)
            result.is_valid = len(result.errors) == 0
            
        except Exception as e:
            result.errors.append(f"验证过程出错: {str(e)}")
            result.is_valid = False
            result.score = 0.0
        
        return result
    
    def _load_config_file(self, file_path: str) -> Optional[Dict[str, Any]]:
        """加载配置文件"""
        try:
            file_ext = Path(file_path).suffix.lower()
            
            with open(file_path, 'r', encoding='utf-8') as f:
                if file_ext in ['.yml', '.yaml']:
                    return yaml.safe_load(f)
                elif file_ext == '.json':
                    return json.load(f)
                elif file_ext == '.env':
                    return self._parse_env_file(f.read())
                else:
                    # 尝试按YAML解析
                    f.seek(0)
                    return yaml.safe_load(f)
                    
        except Exception as e:
            logger.error(f"加载配置文件失败: {e}")
            return None
    
    def _parse_env_file(self, content: str) -> Dict[str, Any]:
        """解析.env文件"""
        config = {}
        for line in content.split('\n'):
            line = line.strip()
            if line and not line.startswith('#'):
                if '=' in line:
                    key, value = line.split('=', 1)
                    key = key.strip()
                    value = value.strip().strip('"\'')
                    
                    # 尝试转换数据类型
                    if value.lower() in ['true', 'false']:
                        value = value.lower() == 'true'
                    elif value.isdigit():
                        value = int(value)
                    elif self._is_float(value):
                        value = float(value)
                    
                    config[key] = value
        return config
    
    def _is_float(self, value: str) -> bool:
        """检查是否为浮点数"""
        try:
            float(value)
            return '.' in value
        except ValueError:
            return False
    
    def _detect_config_type(self, config_data: Dict[str, Any], file_path: str) -> str:
        """自动检测配置类型"""
        # 基于文件名检测
        file_name = Path(file_path).name.lower()
        
        if 'database' in file_name or 'db' in file_name:
            return 'database'
        elif 'redis' in file_name:
            return 'redis'
        
        # 基于配置内容检测
        if 'host' in config_data and 'database' in config_data:
            return 'database'
        elif 'host' in config_data and 'port' in config_data and len(config_data) <= 6:
            return 'redis'
        
        return 'unknown'
    
    def _validate_structure(self, config_data: Dict[str, Any], config_type: str) -> ValidationResult:
        """验证配置结构"""
        result = ValidationResult(True, [], [], [], 100.0)
        
        if config_type not in self.schemas:
            result.warnings.append(f"未知的配置类型: {config_type}")
            return result
        
        schema = self.schemas[config_type]
        
        # 检查必需字段
        for field in schema.required_fields:
            if field not in config_data:
                result.errors.append(f"缺少必需字段: {field}")
        
        # 检查字段类型
        for field, expected_type in schema.field_types.items():
            if field in config_data:
                actual_value = config_data[field]
                if not isinstance(actual_value, expected_type):
                    result.errors.append(
                        f"字段 {field} 类型错误: 期望 {expected_type.__name__}, "
                        f"实际 {type(actual_value).__name__}"
                    )
        
        return result
    
    def _validate_values(self, config_data: Dict[str, Any], config_type: str) -> ValidationResult:
        """验证配置值"""
        result = ValidationResult(True, [], [], [], 100.0)
        
        if config_type not in self.schemas:
            return result
        
        schema = self.schemas[config_type]
        
        # 检查字段模式
        for field, pattern in schema.field_patterns.items():
            if field in config_data:
                value = str(config_data[field])
                if not re.match(pattern, value):
                    result.errors.append(f"字段 {field} 格式不正确: {value}")
        
        # 检查字段范围
        for field, (min_val, max_val) in schema.field_ranges.items():
            if field in config_data:
                value = config_data[field]
                if isinstance(value, (int, float)):
                    if value < min_val or value > max_val:
                        result.errors.append(
                            f"字段 {field} 超出范围 [{min_val}, {max_val}]: {value}"
                        )
        
        return result
    
    def _validate_security(self, config_data: Dict[str, Any], config_type: str) -> ValidationResult:
        """验证安全性"""
        result = ValidationResult(True, [], [], [], 100.0)
        
        security_rules = self.validation_rules['security']
        
        # 检查密码字段
        password_fields = ['password', 'passwd', 'secret', 'token', 'key']
        
        for field in password_fields:
            if field in config_data:
                password = str(config_data[field])
                
                # 检查密码长度
                if len(password) < security_rules['password_min_length']:
                    result.errors.append(
                        f"密码 {field} 长度不足，至少需要 "
                        f"{security_rules['password_min_length']} 个字符"
                    )
                
                # 检查弱密码
                if password.lower() in security_rules['forbidden_passwords']:
                    result.errors.append(f"密码 {field} 过于简单，存在安全风险")
        
        return result
    
    def _merge_results(self, main_result: ValidationResult, other_result: ValidationResult):
        """合并验证结果"""
        main_result.errors.extend(other_result.errors)
        main_result.warnings.extend(other_result.warnings)
        main_result.suggestions.extend(other_result.suggestions)
    
    def _calculate_score(self, result: ValidationResult) -> float:
        """计算配置得分"""
        score = 100.0
        
        # 错误扣分
        score -= len(result.errors) * 20
        
        # 警告扣分
        score -= len(result.warnings) * 5
        
        # 建议扣分（轻微）
        score -= len(result.suggestions) * 1
        
        return max(0.0, score)


# CLI工具
def main():
    """命令行工具入口"""
    import argparse
    
    parser = argparse.ArgumentParser(description='RedFire 配置验证工具')
    parser.add_argument('path', help='配置文件路径')
    parser.add_argument('--type', '-t', help='配置类型 (database, redis)')
    
    args = parser.parse_args()
    
    validator = ConfigValidator()
    result = validator.validate_config_file(args.path, args.type)
    
    print(f"文件: {args.path}")
    print(f"状态: {'有效' if result.is_valid else '无效'}")
    print(f"得分: {result.score:.1f}/100")
    
    if result.errors:
        print("\n错误:")
        for error in result.errors:
            print(f"  - {error}")
    
    if result.warnings:
        print("\n警告:")
        for warning in result.warnings:
            print(f"  - {warning}")


if __name__ == "__main__":
    main()
